home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / gettext / intl / lock.c < prev    next >
Encoding:
C/C++ Source or Header  |  2010-09-19  |  26.2 KB  |  1,060 lines

  1. /* Locking in multithreaded situations.
  2.    Copyright (C) 2005-2008 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify it
  5.    under the terms of the GNU Library General Public License as published
  6.    by the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12.    Library General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU Library General Public
  15.    License along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  17.    USA.  */
  18.  
  19. /* Written by Bruno Haible <bruno@clisp.org>, 2005.
  20.    Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
  21.    gthr-win32.h.  */
  22.  
  23. #include <config.h>
  24.  
  25. #include "lock.h"
  26.  
  27. /* ========================================================================= */
  28.  
  29. #if USE_POSIX_THREADS
  30.  
  31. /* -------------------------- gl_lock_t datatype -------------------------- */
  32.  
  33. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  34.  
  35. # if HAVE_PTHREAD_RWLOCK
  36.  
  37. #  if !defined PTHREAD_RWLOCK_INITIALIZER
  38.  
  39. int
  40. glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
  41. {
  42.   int err;
  43.  
  44.   err = pthread_rwlock_init (&lock->rwlock, NULL);
  45.   if (err != 0)
  46.     return err;
  47.   lock->initialized = 1;
  48.   return 0;
  49. }
  50.  
  51. int
  52. glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
  53. {
  54.   if (!lock->initialized)
  55.     {
  56.       int err;
  57.  
  58.       err = pthread_mutex_lock (&lock->guard);
  59.       if (err != 0)
  60.         return err;
  61.       if (!lock->initialized)
  62.         {
  63.           err = glthread_rwlock_init_multithreaded (lock);
  64.           if (err != 0)
  65.             {
  66.               pthread_mutex_unlock (&lock->guard);
  67.               return err;
  68.             }
  69.         }
  70.       err = pthread_mutex_unlock (&lock->guard);
  71.       if (err != 0)
  72.         return err;
  73.     }
  74.   return pthread_rwlock_rdlock (&lock->rwlock);
  75. }
  76.  
  77. int
  78. glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
  79. {
  80.   if (!lock->initialized)
  81.     {
  82.       int err;
  83.  
  84.       err = pthread_mutex_lock (&lock->guard);
  85.       if (err != 0)
  86.         return err;
  87.       if (!lock->initialized)
  88.         {
  89.           err = glthread_rwlock_init_multithreaded (lock);
  90.           if (err != 0)
  91.             {
  92.               pthread_mutex_unlock (&lock->guard);
  93.               return err;
  94.             }
  95.         }
  96.       err = pthread_mutex_unlock (&lock->guard);
  97.       if (err != 0)
  98.         return err;
  99.     }
  100.   return pthread_rwlock_wrlock (&lock->rwlock);
  101. }
  102.  
  103. int
  104. glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
  105. {
  106.   if (!lock->initialized)
  107.     return EINVAL;
  108.   return pthread_rwlock_unlock (&lock->rwlock);
  109. }
  110.  
  111. int
  112. glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
  113. {
  114.   int err;
  115.  
  116.   if (!lock->initialized)
  117.     return EINVAL;
  118.   err = pthread_rwlock_destroy (&lock->rwlock);
  119.   if (err != 0)
  120.     return err;
  121.   lock->initialized = 0;
  122.   return 0;
  123. }
  124.  
  125. #  endif
  126.  
  127. # else
  128.  
  129. int
  130. glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
  131. {
  132.   int err;
  133.  
  134.   err = pthread_mutex_init (&lock->lock, NULL);
  135.   if (err != 0)
  136.     return err;
  137.   err = pthread_cond_init (&lock->waiting_readers, NULL);
  138.   if (err != 0)
  139.     return err;
  140.   err = pthread_cond_init (&lock->waiting_writers, NULL);
  141.   if (err != 0)
  142.     return err;
  143.   lock->waiting_writers_count = 0;
  144.   lock->runcount = 0;
  145.   return 0;
  146. }
  147.  
  148. int
  149. glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
  150. {
  151.   int err;
  152.  
  153.   err = pthread_mutex_lock (&lock->lock);
  154.   if (err != 0)
  155.     return err;
  156.   /* Test whether only readers are currently running, and whether the runcount
  157.      field will not overflow.  */
  158.   /* POSIX says: "It is implementation-defined whether the calling thread
  159.      acquires the lock when a writer does not hold the lock and there are
  160.      writers blocked on the lock."  Let's say, no: give the writers a higher
  161.      priority.  */
  162.   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
  163.     {
  164.       /* This thread has to wait for a while.  Enqueue it among the
  165.          waiting_readers.  */
  166.       err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
  167.       if (err != 0)
  168.         {
  169.           pthread_mutex_unlock (&lock->lock);
  170.           return err;
  171.         }
  172.     }
  173.   lock->runcount++;
  174.   return pthread_mutex_unlock (&lock->lock);
  175. }
  176.  
  177. int
  178. glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
  179. {
  180.   int err;
  181.  
  182.   err = pthread_mutex_lock (&lock->lock);
  183.   if (err != 0)
  184.     return err;
  185.   /* Test whether no readers or writers are currently running.  */
  186.   while (!(lock->runcount == 0))
  187.     {
  188.       /* This thread has to wait for a while.  Enqueue it among the
  189.          waiting_writers.  */
  190.       lock->waiting_writers_count++;
  191.       err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
  192.       if (err != 0)
  193.         {
  194.           lock->waiting_writers_count--;
  195.           pthread_mutex_unlock (&lock->lock);
  196.           return err;
  197.         }
  198.       lock->waiting_writers_count--;
  199.     }
  200.   lock->runcount--; /* runcount becomes -1 */
  201.   return pthread_mutex_unlock (&lock->lock);
  202. }
  203.  
  204. int
  205. glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
  206. {
  207.   int err;
  208.  
  209.   err = pthread_mutex_lock (&lock->lock);
  210.   if (err != 0)
  211.     return err;
  212.   if (lock->runcount < 0)
  213.     {
  214.       /* Drop a writer lock.  */
  215.       if (!(lock->runcount == -1))
  216.         {
  217.           pthread_mutex_unlock (&lock->lock);
  218.           return EINVAL;
  219.         }
  220.       lock->runcount = 0;
  221.     }
  222.   else
  223.     {
  224.       /* Drop a reader lock.  */
  225.       if (!(lock->runcount > 0))
  226.         {
  227.           pthread_mutex_unlock (&lock->lock);
  228.           return EINVAL;
  229.         }
  230.       lock->runcount--;
  231.     }
  232.   if (lock->runcount == 0)
  233.     {
  234.       /* POSIX recommends that "write locks shall take precedence over read
  235.          locks", to avoid "writer starvation".  */
  236.       if (lock->waiting_writers_count > 0)
  237.         {
  238.           /* Wake up one of the waiting writers.  */
  239.           err = pthread_cond_signal (&lock->waiting_writers);
  240.           if (err != 0)
  241.             {
  242.               pthread_mutex_unlock (&lock->lock);
  243.               return err;
  244.             }
  245.         }
  246.       else
  247.         {
  248.           /* Wake up all waiting readers.  */
  249.           err = pthread_cond_broadcast (&lock->waiting_readers);
  250.           if (err != 0)
  251.             {
  252.               pthread_mutex_unlock (&lock->lock);
  253.               return err;
  254.             }
  255.         }
  256.     }
  257.   return pthread_mutex_unlock (&lock->lock);
  258. }
  259.  
  260. int
  261. glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
  262. {
  263.   int err;
  264.  
  265.   err = pthread_mutex_destroy (&lock->lock);
  266.   if (err != 0)
  267.     return err;
  268.   err = pthread_cond_destroy (&lock->waiting_readers);
  269.   if (err != 0)
  270.     return err;
  271.   err = pthread_cond_destroy (&lock->waiting_writers);
  272.   if (err != 0)
  273.     return err;
  274.   return 0;
  275. }
  276.  
  277. # endif
  278.  
  279. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  280.  
  281. # if HAVE_PTHREAD_MUTEX_RECURSIVE
  282.  
  283. #  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
  284.  
  285. int
  286. glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
  287. {
  288.   pthread_mutexattr_t attributes;
  289.   int err;
  290.  
  291.   err = pthread_mutexattr_init (&attributes);
  292.   if (err != 0)
  293.     return err;
  294.   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
  295.   if (err != 0)
  296.     {
  297.       pthread_mutexattr_destroy (&attributes);
  298.       return err;
  299.     }
  300.   err = pthread_mutex_init (lock, &attributes);
  301.   if (err != 0)
  302.     {
  303.       pthread_mutexattr_destroy (&attributes);
  304.       return err;
  305.     }
  306.   err = pthread_mutexattr_destroy (&attributes);
  307.   if (err != 0)
  308.     return err;
  309.   return 0;
  310. }
  311.  
  312. #  else
  313.  
  314. int
  315. glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
  316. {
  317.   pthread_mutexattr_t attributes;
  318.   int err;
  319.  
  320.   err = pthread_mutexattr_init (&attributes);
  321.   if (err != 0)
  322.     return err;
  323.   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
  324.   if (err != 0)
  325.     {
  326.       pthread_mutexattr_destroy (&attributes);
  327.       return err;
  328.     }
  329.   err = pthread_mutex_init (&lock->recmutex, &attributes);
  330.   if (err != 0)
  331.     {
  332.       pthread_mutexattr_destroy (&attributes);
  333.       return err;
  334.     }
  335.   err = pthread_mutexattr_destroy (&attributes);
  336.   if (err != 0)
  337.     return err;
  338.   lock->initialized = 1;
  339.   return 0;
  340. }
  341.  
  342. int
  343. glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
  344. {
  345.   if (!lock->initialized)
  346.     {
  347.       int err;
  348.  
  349.       err = pthread_mutex_lock (&lock->guard);
  350.       if (err != 0)
  351.         return err;
  352.       if (!lock->initialized)
  353.         {
  354.           err = glthread_recursive_lock_init_multithreaded (lock);
  355.           if (err != 0)
  356.             {
  357.               pthread_mutex_unlock (&lock->guard);
  358.               return err;
  359.             }
  360.         }
  361.       err = pthread_mutex_unlock (&lock->guard);
  362.       if (err != 0)
  363.         return err;
  364.     }
  365.   return pthread_mutex_lock (&lock->recmutex);
  366. }
  367.  
  368. int
  369. glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
  370. {
  371.   if (!lock->initialized)
  372.     return EINVAL;
  373.   return pthread_mutex_unlock (&lock->recmutex);
  374. }
  375.  
  376. int
  377. glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
  378. {
  379.   int err;
  380.  
  381.   if (!lock->initialized)
  382.     return EINVAL;
  383.   err = pthread_mutex_destroy (&lock->recmutex);
  384.   if (err != 0)
  385.     return err;
  386.   lock->initialized = 0;
  387.   return 0;
  388. }
  389.  
  390. #  endif
  391.  
  392. # else
  393.  
  394. int
  395. glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
  396. {
  397.   int err;
  398.  
  399.   err = pthread_mutex_init (&lock->mutex, NULL);
  400.   if (err != 0)
  401.     return err;
  402.   lock->owner = (pthread_t) 0;
  403.   lock->depth = 0;
  404.   return 0;
  405. }
  406.  
  407. int
  408. glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
  409. {
  410.   pthread_t self = pthread_self ();
  411.   if (lock->owner != self)
  412.     {
  413.       int err;
  414.  
  415.       err = pthread_mutex_lock (&lock->mutex);
  416.       if (err != 0)
  417.         return err;
  418.       lock->owner = self;
  419.     }
  420.   if (++(lock->depth) == 0) /* wraparound? */
  421.     {
  422.       lock->depth--;
  423.       return EAGAIN;
  424.     }
  425.   return 0;
  426. }
  427.  
  428. int
  429. glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
  430. {
  431.   if (lock->owner != pthread_self ())
  432.     return EPERM;
  433.   if (lock->depth == 0)
  434.     return EINVAL;
  435.   if (--(lock->depth) == 0)
  436.     {
  437.       lock->owner = (pthread_t) 0;
  438.       return pthread_mutex_unlock (&lock->mutex);
  439.     }
  440.   else
  441.     return 0;
  442. }
  443.  
  444. int
  445. glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
  446. {
  447.   if (lock->owner != (pthread_t) 0)
  448.     return EBUSY;
  449.   return pthread_mutex_destroy (&lock->mutex);
  450. }
  451.  
  452. # endif
  453.  
  454. /* -------------------------- gl_once_t datatype -------------------------- */
  455.  
  456. static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
  457.  
  458. int
  459. glthread_once_singlethreaded (pthread_once_t *once_control)
  460. {
  461.   /* We don't know whether pthread_once_t is an integer type, a floating-point
  462.      type, a pointer type, or a structure type.  */
  463.   char *firstbyte = (char *)once_control;
  464.   if (*firstbyte == *(const char *)&fresh_once)
  465.     {
  466.       /* First time use of once_control.  Invert the first byte.  */
  467.       *firstbyte = ~ *(const char *)&fresh_once;
  468.       return 1;
  469.     }
  470.   else
  471.     return 0;
  472. }
  473.  
  474. #endif
  475.  
  476. /* ========================================================================= */
  477.  
  478. #if USE_PTH_THREADS
  479.  
  480. /* Use the GNU Pth threads library.  */
  481.  
  482. /* -------------------------- gl_lock_t datatype -------------------------- */
  483.  
  484. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  485.  
  486. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  487.  
  488. /* -------------------------- gl_once_t datatype -------------------------- */
  489.  
  490. static void
  491. glthread_once_call (void *arg)
  492. {
  493.   void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
  494.   void (*initfunction) (void) = *gl_once_temp_addr;
  495.   initfunction ();
  496. }
  497.  
  498. int
  499. glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
  500. {
  501.   void (*temp) (void) = initfunction;
  502.   return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
  503. }
  504.  
  505. int
  506. glthread_once_singlethreaded (pth_once_t *once_control)
  507. {
  508.   /* We know that pth_once_t is an integer type.  */
  509.   if (*once_control == PTH_ONCE_INIT)
  510.     {
  511.       /* First time use of once_control.  Invert the marker.  */
  512.       *once_control = ~ PTH_ONCE_INIT;
  513.       return 1;
  514.     }
  515.   else
  516.     return 0;
  517. }
  518.  
  519. #endif
  520.  
  521. /* ========================================================================= */
  522.  
  523. #if USE_SOLARIS_THREADS
  524.  
  525. /* Use the old Solaris threads library.  */
  526.  
  527. /* -------------------------- gl_lock_t datatype -------------------------- */
  528.  
  529. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  530.  
  531. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  532.  
  533. int
  534. glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
  535. {
  536.   int err;
  537.  
  538.   err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
  539.   if (err != 0)
  540.     return err;
  541.   lock->owner = (thread_t) 0;
  542.   lock->depth = 0;
  543.   return 0;
  544. }
  545.  
  546. int
  547. glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
  548. {
  549.   thread_t self = thr_self ();
  550.   if (lock->owner != self)
  551.     {
  552.       int err;
  553.  
  554.       err = mutex_lock (&lock->mutex);
  555.       if (err != 0)
  556.         return err;
  557.       lock->owner = self;
  558.     }
  559.   if (++(lock->depth) == 0) /* wraparound? */
  560.     {
  561.       lock->depth--;
  562.       return EAGAIN;
  563.     }
  564.   return 0;
  565. }
  566.  
  567. int
  568. glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
  569. {
  570.   if (lock->owner != thr_self ())
  571.     return EPERM;
  572.   if (lock->depth == 0)
  573.     return EINVAL;
  574.   if (--(lock->depth) == 0)
  575.     {
  576.       lock->owner = (thread_t) 0;
  577.       return mutex_unlock (&lock->mutex);
  578.     }
  579.   else
  580.     return 0;
  581. }
  582.  
  583. int
  584. glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
  585. {
  586.   if (lock->owner != (thread_t) 0)
  587.     return EBUSY;
  588.   return mutex_destroy (&lock->mutex);
  589. }
  590.  
  591. /* -------------------------- gl_once_t datatype -------------------------- */
  592.  
  593. int
  594. glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
  595. {
  596.   if (!once_control->inited)
  597.     {
  598.       int err;
  599.  
  600.       /* Use the mutex to guarantee that if another thread is already calling
  601.          the initfunction, this thread waits until it's finished.  */
  602.       err = mutex_lock (&once_control->mutex);
  603.       if (err != 0)
  604.         return err;
  605.       if (!once_control->inited)
  606.         {
  607.           once_control->inited = 1;
  608.           initfunction ();
  609.         }
  610.       return mutex_unlock (&once_control->mutex);
  611.     }
  612.   else
  613.     return 0;
  614. }
  615.  
  616. int
  617. glthread_once_singlethreaded (gl_once_t *once_control)
  618. {
  619.   /* We know that gl_once_t contains an integer type.  */
  620.   if (!once_control->inited)
  621.     {
  622.       /* First time use of once_control.  Invert the marker.  */
  623.       once_control->inited = ~ 0;
  624.       return 1;
  625.     }
  626.   else
  627.     return 0;
  628. }
  629.  
  630. #endif
  631.  
  632. /* ========================================================================= */
  633.  
  634. #if USE_WIN32_THREADS
  635.  
  636. /* -------------------------- gl_lock_t datatype -------------------------- */
  637.  
  638. void
  639. glthread_lock_init_func (gl_lock_t *lock)
  640. {
  641.   InitializeCriticalSection (&lock->lock);
  642.   lock->guard.done = 1;
  643. }
  644.  
  645. int
  646. glthread_lock_lock_func (gl_lock_t *lock)
  647. {
  648.   if (!lock->guard.done)
  649.     {
  650.       if (InterlockedIncrement (&lock->guard.started) == 0)
  651.         /* This thread is the first one to need this lock.  Initialize it.  */
  652.         glthread_lock_init (lock);
  653.       else
  654.         /* Yield the CPU while waiting for another thread to finish
  655.            initializing this lock.  */
  656.         while (!lock->guard.done)
  657.           Sleep (0);
  658.     }
  659.   EnterCriticalSection (&lock->lock);
  660.   return 0;
  661. }
  662.  
  663. int
  664. glthread_lock_unlock_func (gl_lock_t *lock)
  665. {
  666.   if (!lock->guard.done)
  667.     return EINVAL;
  668.   LeaveCriticalSection (&lock->lock);
  669.   return 0;
  670. }
  671.  
  672. int
  673. glthread_lock_destroy_func (gl_lock_t *lock)
  674. {
  675.   if (!lock->guard.done)
  676.     return EINVAL;
  677.   DeleteCriticalSection (&lock->lock);
  678.   lock->guard.done = 0;
  679.   return 0;
  680. }
  681.  
  682. /* ------------------------- gl_rwlock_t datatype ------------------------- */
  683.  
  684. /* In this file, the waitqueues are implemented as circular arrays.  */
  685. #define gl_waitqueue_t gl_carray_waitqueue_t
  686.  
  687. static inline void
  688. gl_waitqueue_init (gl_waitqueue_t *wq)
  689. {
  690.   wq->array = NULL;
  691.   wq->count = 0;
  692.   wq->alloc = 0;
  693.   wq->offset = 0;
  694. }
  695.  
  696. /* Enqueues the current thread, represented by an event, in a wait queue.
  697.    Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
  698. static HANDLE
  699. gl_waitqueue_add (gl_waitqueue_t *wq)
  700. {
  701.   HANDLE event;
  702.   unsigned int index;
  703.  
  704.   if (wq->count == wq->alloc)
  705.     {
  706.       unsigned int new_alloc = 2 * wq->alloc + 1;
  707.       HANDLE *new_array =
  708.         (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
  709.       if (new_array == NULL)
  710.         /* No more memory.  */
  711.         return INVALID_HANDLE_VALUE;
  712.       /* Now is a good opportunity to rotate the array so that its contents
  713.          starts at offset 0.  */
  714.       if (wq->offset > 0)
  715.         {
  716.           unsigned int old_count = wq->count;
  717.           unsigned int old_alloc = wq->alloc;
  718.           unsigned int old_offset = wq->offset;
  719.           unsigned int i;
  720.           if (old_offset + old_count > old_alloc)
  721.             {
  722.               unsigned int limit = old_offset + old_count - old_alloc;
  723.               for (i = 0; i < limit; i++)
  724.                 new_array[old_alloc + i] = new_array[i];
  725.             }
  726.           for (i = 0; i < old_count; i++)
  727.             new_array[i] = new_array[old_offset + i];
  728.           wq->offset = 0;
  729.         }
  730.       wq->array = new_array;
  731.       wq->alloc = new_alloc;
  732.     }
  733.   /* Whether the created event is a manual-reset one or an auto-reset one,
  734.      does not matter, since we will wait on it only once.  */
  735.   event = CreateEvent (NULL, TRUE, FALSE, NULL);
  736.   if (event == INVALID_HANDLE_VALUE)
  737.     /* No way to allocate an event.  */
  738.     return INVALID_HANDLE_VALUE;
  739.   index = wq->offset + wq->count;
  740.   if (index >= wq->alloc)
  741.     index -= wq->alloc;
  742.   wq->array[index] = event;
  743.   wq->count++;
  744.   return event;
  745. }
  746.  
  747. /* Notifies the first thread from a wait queue and dequeues it.  */
  748. static inline void
  749. gl_waitqueue_notify_first (gl_waitqueue_t *wq)
  750. {
  751.   SetEvent (wq->array[wq->offset + 0]);
  752.   wq->offset++;
  753.   wq->count--;
  754.   if (wq->count == 0 || wq->offset == wq->alloc)
  755.     wq->offset = 0;
  756. }
  757.  
  758. /* Notifies all threads from a wait queue and dequeues them all.  */
  759. static inline void
  760. gl_waitqueue_notify_all (gl_waitqueue_t *wq)
  761. {
  762.   unsigned int i;
  763.  
  764.   for (i = 0; i < wq->count; i++)
  765.     {
  766.       unsigned int index = wq->offset + i;
  767.       if (index >= wq->alloc)
  768.         index -= wq->alloc;
  769.       SetEvent (wq->array[index]);
  770.     }
  771.   wq->count = 0;
  772.   wq->offset = 0;
  773. }
  774.  
  775. void
  776. glthread_rwlock_init_func (gl_rwlock_t *lock)
  777. {
  778.   InitializeCriticalSection (&lock->lock);
  779.   gl_waitqueue_init (&lock->waiting_readers);
  780.   gl_waitqueue_init (&lock->waiting_writers);
  781.   lock->runcount = 0;
  782.   lock->guard.done = 1;
  783. }
  784.  
  785. int
  786. glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
  787. {
  788.   if (!lock->guard.done)
  789.     {
  790.       if (InterlockedIncrement (&lock->guard.started) == 0)
  791.         /* This thread is the first one to need this lock.  Initialize it.  */
  792.         glthread_rwlock_init (lock);
  793.       else
  794.         /* Yield the CPU while waiting for another thread to finish
  795.            initializing this lock.  */
  796.         while (!lock->guard.done)
  797.           Sleep (0);
  798.     }
  799.   EnterCriticalSection (&lock->lock);
  800.   /* Test whether only readers are currently running, and whether the runcount
  801.      field will not overflow.  */
  802.   if (!(lock->runcount + 1 > 0))
  803.     {
  804.       /* This thread has to wait for a while.  Enqueue it among the
  805.          waiting_readers.  */
  806.       HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
  807.       if (event != INVALID_HANDLE_VALUE)
  808.         {
  809.           DWORD result;
  810.           LeaveCriticalSection (&lock->lock);
  811.           /* Wait until another thread signals this event.  */
  812.           result = WaitForSingleObject (event, INFINITE);
  813.           if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
  814.             abort ();
  815.           CloseHandle (event);
  816.           /* The thread which signalled the event already did the bookkeeping:
  817.              removed us from the waiting_readers, incremented lock->runcount.  */
  818.           if (!(lock->runcount > 0))
  819.             abort ();
  820.           return 0;
  821.         }
  822.       else
  823.         {
  824.           /* Allocation failure.  Weird.  */
  825.           do
  826.             {
  827.               LeaveCriticalSection (&lock->lock);
  828.               Sleep (1);
  829.               EnterCriticalSection (&lock->lock);
  830.             }
  831.           while (!(lock->runcount + 1 > 0));
  832.         }
  833.     }
  834.   lock->runcount++;
  835.   LeaveCriticalSection (&lock->lock);
  836.   return 0;
  837. }
  838.  
  839. int
  840. glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
  841. {
  842.   if (!lock->guard.done)
  843.     {
  844.       if (InterlockedIncrement (&lock->guard.started) == 0)
  845.         /* This thread is the first one to need this lock.  Initialize it.  */
  846.         glthread_rwlock_init (lock);
  847.       else
  848.         /* Yield the CPU while waiting for another thread to finish
  849.            initializing this lock.  */
  850.         while (!lock->guard.done)
  851.           Sleep (0);
  852.     }
  853.   EnterCriticalSection (&lock->lock);
  854.   /* Test whether no readers or writers are currently running.  */
  855.   if (!(lock->runcount == 0))
  856.     {
  857.       /* This thread has to wait for a while.  Enqueue it among the
  858.          waiting_writers.  */
  859.       HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
  860.       if (event != INVALID_HANDLE_VALUE)
  861.         {
  862.           DWORD result;
  863.           LeaveCriticalSection (&lock->lock);
  864.           /* Wait until another thread signals this event.  */
  865.           result = WaitForSingleObject (event, INFINITE);
  866.           if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
  867.             abort ();
  868.           CloseHandle (event);
  869.           /* The thread which signalled the event already did the bookkeeping:
  870.              removed us from the waiting_writers, set lock->runcount = -1.  */
  871.           if (!(lock->runcount == -1))
  872.             abort ();
  873.           return 0;
  874.         }
  875.       else
  876.         {
  877.           /* Allocation failure.  Weird.  */
  878.           do
  879.             {
  880.               LeaveCriticalSection (&lock->lock);
  881.               Sleep (1);
  882.               EnterCriticalSection (&lock->lock);
  883.             }
  884.           while (!(lock->runcount == 0));
  885.         }
  886.     }
  887.   lock->runcount--; /* runcount becomes -1 */
  888.   LeaveCriticalSection (&lock->lock);
  889.   return 0;
  890. }
  891.  
  892. int
  893. glthread_rwlock_unlock_func (gl_rwlock_t *lock)
  894. {
  895.   if (!lock->guard.done)
  896.     return EINVAL;
  897.   EnterCriticalSection (&lock->lock);
  898.   if (lock->runcount < 0)
  899.     {
  900.       /* Drop a writer lock.  */
  901.       if (!(lock->runcount == -1))
  902.         abort ();
  903.       lock->runcount = 0;
  904.     }
  905.   else
  906.     {
  907.       /* Drop a reader lock.  */
  908.       if (!(lock->runcount > 0))
  909.         {
  910.           LeaveCriticalSection (&lock->lock);
  911.           return EPERM;
  912.         }
  913.       lock->runcount--;
  914.     }
  915.   if (lock->runcount == 0)
  916.     {
  917.       /* POSIX recommends that "write locks shall take precedence over read
  918.          locks", to avoid "writer starvation".  */
  919.       if (lock->waiting_writers.count > 0)
  920.         {
  921.           /* Wake up one of the waiting writers.  */
  922.           lock->runcount--;
  923.           gl_waitqueue_notify_first (&lock->waiting_writers);
  924.         }
  925.       else
  926.         {
  927.           /* Wake up all waiting readers.  */
  928.           lock->runcount += lock->waiting_readers.count;
  929.           gl_waitqueue_notify_all (&lock->waiting_readers);
  930.         }
  931.     }
  932.   LeaveCriticalSection (&lock->lock);
  933.   return 0;
  934. }
  935.  
  936. int
  937. glthread_rwlock_destroy_func (gl_rwlock_t *lock)
  938. {
  939.   if (!lock->guard.done)
  940.     return EINVAL;
  941.   if (lock->runcount != 0)
  942.     return EBUSY;
  943.   DeleteCriticalSection (&lock->lock);
  944.   if (lock->waiting_readers.array != NULL)
  945.     free (lock->waiting_readers.array);
  946.   if (lock->waiting_writers.array != NULL)
  947.     free (lock->waiting_writers.array);
  948.   lock->guard.done = 0;
  949.   return 0;
  950. }
  951.  
  952. /* --------------------- gl_recursive_lock_t datatype --------------------- */
  953.  
  954. void
  955. glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
  956. {
  957.   lock->owner = 0;
  958.   lock->depth = 0;
  959.   InitializeCriticalSection (&lock->lock);
  960.   lock->guard.done = 1;
  961. }
  962.  
  963. int
  964. glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
  965. {
  966.   if (!lock->guard.done)
  967.     {
  968.       if (InterlockedIncrement (&lock->guard.started) == 0)
  969.         /* This thread is the first one to need this lock.  Initialize it.  */
  970.         glthread_recursive_lock_init (lock);
  971.       else
  972.         /* Yield the CPU while waiting for another thread to finish
  973.            initializing this lock.  */
  974.         while (!lock->guard.done)
  975.           Sleep (0);
  976.     }
  977.   {
  978.     DWORD self = GetCurrentThreadId ();
  979.     if (lock->owner != self)
  980.       {
  981.         EnterCriticalSection (&lock->lock);
  982.         lock->owner = self;
  983.       }
  984.     if (++(lock->depth) == 0) /* wraparound? */
  985.       {
  986.         lock->depth--;
  987.         return EAGAIN;
  988.       }
  989.   }
  990.   return 0;
  991. }
  992.  
  993. int
  994. glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
  995. {
  996.   if (lock->owner != GetCurrentThreadId ())
  997.     return EPERM;
  998.   if (lock->depth == 0)
  999.     return EINVAL;
  1000.   if (--(lock->depth) == 0)
  1001.     {
  1002.       lock->owner = 0;
  1003.       LeaveCriticalSection (&lock->lock);
  1004.     }
  1005.   return 0;
  1006. }
  1007.  
  1008. int
  1009. glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
  1010. {
  1011.   if (lock->owner != 0)
  1012.     return EBUSY;
  1013.   DeleteCriticalSection (&lock->lock);
  1014.   lock->guard.done = 0;
  1015.   return 0;
  1016. }
  1017.  
  1018. /* -------------------------- gl_once_t datatype -------------------------- */
  1019.  
  1020. void
  1021. glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
  1022. {
  1023.   if (once_control->inited <= 0)
  1024.     {
  1025.       if (InterlockedIncrement (&once_control->started) == 0)
  1026.         {
  1027.           /* This thread is the first one to come to this once_control.  */
  1028.           InitializeCriticalSection (&once_control->lock);
  1029.           EnterCriticalSection (&once_control->lock);
  1030.           once_control->inited = 0;
  1031.           initfunction ();
  1032.           once_control->inited = 1;
  1033.           LeaveCriticalSection (&once_control->lock);
  1034.         }
  1035.       else
  1036.         {
  1037.           /* Undo last operation.  */
  1038.           InterlockedDecrement (&once_control->started);
  1039.           /* Some other thread has already started the initialization.
  1040.              Yield the CPU while waiting for the other thread to finish
  1041.              initializing and taking the lock.  */
  1042.           while (once_control->inited < 0)
  1043.             Sleep (0);
  1044.           if (once_control->inited <= 0)
  1045.             {
  1046.               /* Take the lock.  This blocks until the other thread has
  1047.                  finished calling the initfunction.  */
  1048.               EnterCriticalSection (&once_control->lock);
  1049.               LeaveCriticalSection (&once_control->lock);
  1050.               if (!(once_control->inited > 0))
  1051.                 abort ();
  1052.             }
  1053.         }
  1054.     }
  1055. }
  1056.  
  1057. #endif
  1058.  
  1059. /* ========================================================================= */
  1060.